package api

import (
	"context"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"github.com/go-redis/redis/v8"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"mxshop_api/user-web/forms"
	"mxshop_api/user-web/global"
	"mxshop_api/user-web/global/response"
	"mxshop_api/user-web/middlewares"
	"mxshop_api/user-web/models"
	"mxshop_api/user-web/proto"
	"net/http"
	"strconv"
	"strings"
	"time"
)

func HandlerGrpcToHttp(err error, ctx *gin.Context) {
	if err != nil {
		fromError, ok := status.FromError(err)
		if ok {
			switch fromError.Code() {
			case codes.NotFound:
				ctx.JSON(http.StatusNotFound, gin.H{
					"msg": fromError.Message(),
				})
			case codes.Internal:
				ctx.JSON(http.StatusInternalServerError, gin.H{
					"msg": "内部错误",
				})
			case codes.InvalidArgument:
				ctx.JSON(http.StatusBadRequest, gin.H{
					"msg": "参数错误",
				})
			case codes.Unavailable:
				ctx.JSON(http.StatusInternalServerError, gin.H{
					"msg": "用户服务不可用",
				})
			default:
				ctx.JSON(http.StatusInternalServerError, gin.H{
					"msg": fromError.Message(),
				})
			}
		}
	}

}

func HandleValidatorError(ctx *gin.Context, err error) {

	errs, ok := err.(validator.ValidationErrors)
	if !ok {
		ctx.JSON(http.StatusOK, gin.H{
			"msg": errs.Error(),
		})
	}
	ctx.JSON(http.StatusBadRequest, gin.H{
		"error": removeTopStruct(errs.Translate(global.Trans)),
	})
}

func removeTopStruct(fields map[string]string) map[string]string {
	resp := map[string]string{}
	for field, err := range fields {
		resp[field[strings.Index(field, ".")+1:]] = err
	}
	return resp
}

func GetUserList(ctx *gin.Context) {

	pn := ctx.DefaultQuery("pn", "0")
	pnInt, _ := strconv.Atoi(pn)
	pSize := ctx.DefaultQuery("psize", "10")
	pSizeInt, _ := strconv.Atoi(pSize)

	claims, _ := ctx.Get("claims")
	customClaims := claims.(*models.CustomClaims)
	zap.S().Info("访问用户：%d", customClaims.ID)

	resp, err := global.SrvConn.GetUserList(ctx, &proto.PageInfo{
		Pn:    int32(pnInt),
		PSize: int32(pSizeInt),
	})
	if err != nil {
		zap.S().Errorw("[GetUserList] 查询 【用户列表】失败")
		HandlerGrpcToHttp(err, ctx)
		return
	}
	result := make([]interface{}, 0)
	var userRsp response.UserResponse

	for _, value := range resp.Data {
		userRsp.Id = int32(value.Id)
		userRsp.NickName = value.NickName
		userRsp.Birthday = time.Unix(int64(value.Birthday), 0).Format("2006-01-02")
		userRsp.Gender = value.Gender
		userRsp.Mobile = value.Mobile
		result = append(result, userRsp)
	}
	ctx.JSON(http.StatusOK, gin.H{
		"data":  result,
		"total": resp.Total,
	})
}

func PasswordLogin(ctx *gin.Context) {

	passwordLoginForm := forms.PasswordLoginForm{}
	if err := ctx.ShouldBind(&passwordLoginForm); err != nil {
		HandleValidatorError(ctx, err)
		return
	}

	if verify := store.Verify(passwordLoginForm.CaptchaId, passwordLoginForm.Captcha, false); !verify {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"captcha": "验证码错误",
		})
		return
	}

	if rsp, err := global.SrvConn.GetUserByMobile(context.Background(), &proto.MobileRequest{Mobile: passwordLoginForm.Mobile}); err != nil {
		if e, ok := status.FromError(err); ok {
			switch e.Code() {
			case codes.NotFound:
				ctx.JSON(http.StatusBadRequest, map[string]string{
					"mobile": "用户不存在",
				})
			default:
				ctx.JSON(http.StatusInternalServerError, map[string]string{
					"mobile": "登录失败",
				})
			}
			return
		}
	} else {
		//密码验证
		if checkRsp, err := global.SrvConn.CheckPassword(context.Background(), &proto.PasswordCheckInfo{
			Password:          passwordLoginForm.Password,
			EncryptedPassword: rsp.Password,
		}); err != nil {
			ctx.JSON(http.StatusInternalServerError, map[string]string{
				"msg": "登录失败",
			})
		} else {
			if checkRsp.Success {
				j := middlewares.NewJWT()
				claims := models.CustomClaims{
					ID:          uint(rsp.Id),
					NickName:    rsp.NickName,
					AuthorityId: uint(rsp.Role),
					StandardClaims: jwt.StandardClaims{
						NotBefore: time.Now().Unix(),
						ExpiresAt: time.Now().Unix() + 60*60*24*30,
						Issuer:    "imooc",
					},
				}
				token, err := j.CreateToken(claims)
				if err != nil {
					ctx.JSON(http.StatusInternalServerError, gin.H{
						"msg": "生成token失败",
					})
				}
				ctx.JSON(http.StatusOK, gin.H{
					"id":         rsp.Id,
					"nick_name":  rsp.NickName,
					"token":      token,
					"expired_at": (time.Now().Unix() + 60*60*24*30) * 1000,
				})
			} else {
				ctx.JSON(http.StatusBadRequest, map[string]string{
					"msg": "登录失败",
				})
			}
		}
	}

}

func Register(ctx *gin.Context) {

	var registerForm = forms.RegisterForm{}
	if err := ctx.ShouldBind(registerForm); err != nil {
		HandleValidatorError(ctx, err)
		return
	}
	//验证码
	rdb := redis.NewClient(&redis.Options{
		Addr: fmt.Sprintf("%s:%d", global.ServerConfig.RedisInfo.Host, global.ServerConfig.RedisInfo.Port),
	})
	code, err := rdb.Get(context.Background(), registerForm.Mobile).Result()
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg": "验证码获取失败",
		})
		return
	} else {
		if code != registerForm.Code {
			ctx.JSON(http.StatusInternalServerError, gin.H{
				"msg": "验证码错误",
			})
			return
		}
	}
	//创建用户
	user, err := global.SrvConn.CreateUser(context.Background(), &proto.CreateUserInfo{
		Mobile:   registerForm.Mobile,
		Password: registerForm.Password,
		NickName: registerForm.Mobile,
	})
	if err != nil {
		zap.S().Errorf("[Register] 查询 【新建用户失败】失败: %s", err.Error())
		HandlerGrpcToHttp(err, ctx)
		return
	}
	//生成jwt
	newJWT := middlewares.NewJWT()
	token, err := newJWT.CreateToken(models.CustomClaims{
		ID:          uint(user.Id),
		NickName:    user.NickName,
		AuthorityId: uint(user.Role),
		StandardClaims: jwt.StandardClaims{
			NotBefore: time.Now().Unix(),
			ExpiresAt: time.Now().Unix() + 3600*24*30,
			Issuer:    "imooc",
		},
	})
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg": "token生成失败",
		})
		return
	}
	//返回前端
	ctx.JSON(http.StatusOK, gin.H{
		"id":         user.Id,
		"nick_name":  user.NickName,
		"token":      token,
		"expired_at": (time.Now().Unix() + 3600*24*30) * 1000,
	})
}
